package com.psp.task.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import com.psp.stock.domain.Stock;
import com.psp.stock.service.IStockService;
import com.psp.stockinterface.domain.StockInterface;
import com.psp.stockinterface.service.IStockInterfaceService;
import com.psp.stockprice.domain.StockPrice;
import com.psp.stockprice.dto.StockHistoryPriceDTO;
import com.psp.stockprice.mapper.StockPriceMapper;
import com.psp.stockprice.service.IStockPriceService;
import com.psp.stockrisefallnum.domain.StockRiseFallNum;
import com.psp.stockrisefallnum.service.IStockRiseFallNumService;
import com.psp.stockupstatistics.domain.StockUpStatistics;
import com.psp.stockupstatistics.service.IStockUpStatisticsService;
import com.psp.task.service.IStockTaskService;
import com.psp.util.StockIndexUtils;
import com.psp.util.TradeDateUtils;
import com.psp.util.StockUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author tiger
 */
@Service
public class StockTaskServiceImpl implements IStockTaskService {

    protected static final Logger logger = LoggerFactory.getLogger(StockTaskServiceImpl.class);

    @Resource
    private IStockService stockService;

    @Resource
    private IStockInterfaceService stockInterfaceService;

    @Resource
    private IStockPriceService stockPriceService;

    @Resource
    private StockPriceMapper stockPriceMapper;

    @Resource
    private IStockUpStatisticsService stockUpStatisticsService;

    @Resource
    private IStockRiseFallNumService stockRiseFallNumService;

    @Resource
    private TradeDateUtils tradeDateUtils;

    /**
     * 通过百度获取历史股价
     */
    @Override
    public void getStockHistoryPriceByBaiDu() {
        List<Stock> stockList = stockService.selectStockList(new Stock());
        insertStockHistoryPriceByBaiDu(stockList);
    }

    /**
     * 通过百度获取ETF历史股价
     */
    @Override
    public void getETFHistoryPriceByBaiDu() {
        Stock query = new Stock();
        query.setStockExchange("fund");
        List<Stock> stockList = stockService.selectStockList(query);
        insertStockHistoryPriceByBaiDu(stockList);
    }

    public void insertStockHistoryPriceByBaiDu(List<Stock> stockList) {
        logger.debug("开始获取股票今日价格");
        List<List<String>> price;
        for (Stock stock : stockList) {
            //第三方接口获取股票的历史价格
            price = StockUtils.getStockClosePriceByBaidu(stock.getStockCode());
            if (price == null) {
                logger.debug("获取股票{}价格失败!", stock.getStockCode());
                continue;
            }
            stockPriceService.insertOrUpdateStockHistoryPrice(stock, price);
        }
    }

    /**
     * 通过Sina获取实时股价
     */
    @Override
    public void getStockTodayPriceBySina() {
        List<Stock> stockList = stockService.selectStockListExcludeFund();

        logger.debug("开始获取股票今日价格");

        for (Stock stock : stockList) {
            String[] result = StockUtils.getStockPriceBySina(stock.getStockExchange() + stock.getStockCode());
            if (result == null || result.length == 0) {
                logger.error("获取股票{}价格失败!", stock.getStockCode());
                continue;
            }
            StockPrice stockPrice = new StockPrice();
            stockPrice.setStockCode(stock.getStockCode());
            stockPrice.setTradeDate(DateUtil.parse(result[0]));
            stockPrice.setOpenPrice(Double.valueOf(result[1]));
            if (stockPrice.getOpenPrice() == 0) {
                //价格为0处于停盘状态
                continue;
            }
            stockPrice.setVolume(Double.valueOf(result[5]));
            stockPrice.setClosePrice(Double.valueOf(result[2]));
            stockPrice.setHighest(Double.valueOf(result[3]));
            stockPrice.setLowest(Double.valueOf(result[4]));

            Long id = stockPriceService.selectByStockCodeAndTradeDate(stockPrice.getStockCode(), stockPrice.getTradeDate());
            if (id != null) {
                logger.debug("存在该日期的股票价格,更新价格");
                stockPrice.setId(id);
                stockPrice.setUpdateTime(DateUtil.date());
                stockPriceService.updateStockPrice(stockPrice);
            } else {
                logger.debug("不存在该日期的股票价格,插入价格");
                stockPrice.setCreateTime(DateUtil.date());
                stockPriceService.insertStockPrice(stockPrice);
            }
        }
    }

    /**
     * 更新股票的最新价格和涨跌幅
     */
    @Override
    public void updateStockTodayPriceAndRice() {
        //获取最近两天的股票交易日期
        Date[] recentDate = tradeDateUtils.selectStockTradeStartAndEndDate(2);
        Date startDate = recentDate[0];
        Date endDate = recentDate[1];

        //获取最近两天的股票交易价格
        List<StockPrice> twoDayStockPrice = stockPriceService.selectStockPriceByStartAndEndDate(startDate, endDate);

        List<Stock> updateList = new ArrayList<>();

        StockPrice last = new StockPrice();
        StockPrice current;
        // 从第二条记录开始遍历,后一个和前一个比较,如果不相等,则更新该股票的当前价和涨跌幅
        for (int i = 0; i < twoDayStockPrice.size() - 1; i++) {
            current = twoDayStockPrice.get(i);
            //当前和前一个的股票编码不同，则更新
            if (!current.getStockCode().equals(last.getStockCode())) {
                Stock s = new Stock();
                //当前和下一个相同,则有涨跌幅
                if (current.getStockCode().equals(twoDayStockPrice.get(i + 1).getStockCode())) {
                    if (DateUtil.compare(current.getTradeDate(), twoDayStockPrice.get(i + 1).getTradeDate()) > 0) {
                        //当前日期大于前一个日期
                        s.setCurrentPrice(current.getClosePrice());
                        s.setPriceRiseRange((current.getClosePrice() - twoDayStockPrice.get(i + 1).getClosePrice()) / twoDayStockPrice.get(i + 1).getClosePrice());
                    } else {
                        s.setCurrentPrice(twoDayStockPrice.get(i + 1).getClosePrice());
                        s.setPriceRiseRange((twoDayStockPrice.get(i + 1).getClosePrice() - current.getClosePrice()) / current.getClosePrice());
                    }
                } else {
                    //当前和下一个不相同,则该股票只有一条记录，只更新价格没有涨跌幅
                    s.setCurrentPrice(current.getClosePrice());
                }
                s.setStockCode(twoDayStockPrice.get(i).getStockCode());
                updateList.add(s);
            }
            last = twoDayStockPrice.get(i);
        }

        //处理最后一个,如果最后一个和前一个不相同
        if (!twoDayStockPrice.get(twoDayStockPrice.size() - 1).getStockCode().equals(twoDayStockPrice.get(twoDayStockPrice.size() - 2).getStockCode())) {
            Stock s = new Stock();
            s.setCurrentPrice(twoDayStockPrice.get(twoDayStockPrice.size() - 1).getClosePrice());
            updateList.add(s);
        }
        stockService.batchUpdateStock(updateList);
        logger.info("更新股票的最新价格和涨跌幅");
    }

    /**
     * 统计一段时间内的股票波动
     */
    @Override
    public void statisticsStockPriceOverAPeriodOfTime(Integer statisticDays) {
        logger.debug("清空数据");
        stockUpStatisticsService.clearData();

        Date[] tradeDate = tradeDateUtils.selectStockTradeStartAndEndDate(statisticDays);
        //统计开始日期
        Date startDate = tradeDate[0];
        //统计结束日期
        Date endDate = tradeDate[1];

        logger.debug("获取所有股票一段天数内的历史股价");
        List<StockHistoryPriceDTO> stockHistoryPriceDTOList = getStockHistoryPriceDTO(startDate, endDate);

        List<StockUpStatistics> insertList = new ArrayList<>();
        if (stockHistoryPriceDTOList != null && stockHistoryPriceDTOList.size() > 1) {
            for (StockHistoryPriceDTO dto : stockHistoryPriceDTOList) {
                StockUpStatistics statistics = new StockUpStatistics();
                //涨跌统计天数
                statistics.setStatisticsDays(statisticDays);
                statistics.setStockCode(dto.getStockCode());
                //开盘价、收盘价、最高价、最低价
                List<Double> openPriceList = dto.getOpenPriceList();
                List<Double> closePriceList = dto.getClosePriceList();
                List<Double> highestList = dto.getHighestList();
                List<Double> lowestList = dto.getLowestList();
                //设置5日、10日等均线
                statistics.setClosePrice(closePriceList.size() > 0 ? closePriceList.get(closePriceList.size() - 1) : null);
                statistics.setMa5(getAverageLine(closePriceList, 5));
                statistics.setMa10(getAverageLine(closePriceList, 10));
                statistics.setMa20(getAverageLine(closePriceList, 20));
                statistics.setMa30(getAverageLine(closePriceList, 30));
                statistics.setMa60(getAverageLine(closePriceList, 60));
                statistics.setMa125(getAverageLine(closePriceList, 125));
                statistics.setMa250(getAverageLine(closePriceList, 250));
                // 设置类型,1涨停2跌停3光头4光头光脚
                statistics.setType(getType(openPriceList, closePriceList, highestList, lowestList));
                //设置涨跌的天数
                setStockPriceRiseAndFall(statistics, closePriceList);
                //设置成交量倍数
                setVolumeTimes(statistics, dto.getVolumeList());
                //设置MACD金叉个数和当前是否金叉1是0否
                setMacdFork(statistics, closePriceList);
                //设置KDJ金叉个数和当前是否金叉1是0否
                setKdjFork(statistics, dto.getPriceList().stream()
                        .map(row -> row.stream().mapToDouble(Double::doubleValue).toArray())
                        .toArray(double[][]::new));
                insertList.add(statistics);
            }
            //设置股票名称
            List<Stock> allStockList = stockService.selectStockList(new Stock());
            insertList.forEach(n -> n.setStockName(allStockList.stream().filter(i -> i.getStockCode().equals(n.getStockCode())).findFirst().orElse(new Stock()).getStockName()));
            stockUpStatisticsService.batchInsertStockUpStatistics(insertList);
        }
    }

    private void setVolumeTimes(StockUpStatistics statistics, List<Double> volumeList) {
        if (volumeList.size() < 3) return;
        int size = volumeList.size();
        if (volumeList.get(size - 1) == 0 || volumeList.get(size - 2) == 0 || volumeList.get(size - 3) == 0) return;
        double d1 = volumeList.get(size - 1) / volumeList.get(size - 2);
        double d2 = volumeList.get(size - 2) / volumeList.get(size - 3);
        statistics.setVolumeTimes(Math.floor(Math.max(d1, d2)));
    }

    /**
     * 设置MACD金叉个数和当前是否金叉1是0否
     *
     * @param statistics     统计对象
     * @param closePriceList 收盘价集合
     */
    private void setMacdFork(StockUpStatistics statistics, List<Double> closePriceList) {
        double[] c = closePriceList.stream().mapToDouble(Double::doubleValue).toArray();
        Map<String, double[]> macdMap = StockIndexUtils.calculateMACD(c);
        if (closePriceList.size() < 2) return;
        double[] dif = macdMap.get("dif");
        double[] dea = macdMap.get("dea");
        double[] macd = macdMap.get("macd");
        int length = dif.length;
        int forkTimes = 0;

        //设置macd是否大于0
        statistics.setMacdGreaterThan0(macd[length - 1] > 0 ? 1 : 0);

        //设置macd金叉次数
        for (int i = 0; i < length - 2; i++) {
            if (dif[i] < dea[i] && dif[i + 2] > dea[i + 2]) {
                forkTimes++;
            }
        }
        statistics.setMacdGoldForkTimes(forkTimes);

        //设置macd金叉状态
        if (dif[length - 1] > dea[length - 1]) {
            statistics.setInMacdGoldFork(1);
        } else {
            statistics.setInMacdGoldFork(0);
        }
    }

    /**
     * 设置KDJ金叉个数和当前是否金叉1是0否
     *
     * @param statistics 统计对象
     * @param data       收盘价集合
     */
    private void setKdjFork(StockUpStatistics statistics, double[][] data) {
        Map<String, double[]> kdjMap = StockIndexUtils.calculateKDJ(data);
        if (data.length < 5) return;
        double[] k = kdjMap.get("kVal");
        double[] d = kdjMap.get("dVal");
        double[] j = kdjMap.get("jVal");
        int length = k.length;

        //设置dkj在5天内是否超跌
        statistics.setKdjOverfall(0);
        for (int i = length - 1; i > length - 6; i--) {
            if (k[i] < 20 && d[i] < 20 && j[i] < 0) {
                statistics.setKdjOverfall(1);
            }
        }

        //设置kdj金叉次数
        int forkTimes = 0;
        for (int i = 0; i < length - 2; i++) {
            if (k[i] < d[i] && k[i + 2] > d[i + 2]) {
                forkTimes++;
            }
        }
        statistics.setKdjGoldForkTimes(forkTimes);

        //设置kdj金叉状态
        if (k[length - 1] > d[length - 1]) {
            statistics.setInKdjGoldFork(1);
        } else {
            statistics.setInKdjGoldFork(0);
        }
    }

    /**
     * 设置涨跌天数、幅度等
     */
    private void setStockPriceRiseAndFall(StockUpStatistics statistics, List<Double> closePriceList) {
        if (closePriceList.size() > 1) {
            //涨的天数
            int riseDays = 0;
            //跌的天数
            int fallDays = 0;
            //涨停天数
            int riseStopDays = 0;
            //跌停天数
            int fallStopDays = 0;
            for (int i = 1; i < closePriceList.size(); i++) {
                if (closePriceList.get(i) > closePriceList.get(i - 1)) {
                    riseDays++;
                } else {
                    fallDays++;
                }
                if (NumberUtil.round(closePriceList.get(i - 1) * 1.1, 2).equals(NumberUtil.round(closePriceList.get(i), 2))) {
                    riseStopDays++;
                }
                if (NumberUtil.round(closePriceList.get(i - 1) * 0.9, 2).equals(NumberUtil.round(closePriceList.get(i), 2))) {
                    fallStopDays++;
                }
            }
            //连涨天数
            int continueRiseDays = 0;
            boolean continueRiseDaysFlag = true;
            //连跌天数
            int continueFallDays = 0;
            boolean continueFallDaysFlag = true;
            //连续涨停天数
            int continueRiseStopDays = 0;
            boolean continueRiseStopDaysFlag = true;
            //连续跌停天数
            int continueFallStopDays = 0;
            boolean continueFallStopDaysFlag = true;
            double closePrice = closePriceList.get(closePriceList.size() - 1);
            for (int i = closePriceList.size() - 1; i > 0; i--) {
                if (continueRiseDaysFlag) {
                    //连涨天数
                    if (closePriceList.get(i) > closePriceList.get(i - 1)) {
                        continueRiseDays++;
                    } else {
                        continueRiseDaysFlag = false;
                        //连涨幅度
                        statistics.setContinueRiseRate((closePrice - closePriceList.get(i)) / closePriceList.get(i) * 100);
                    }
                }
                if (continueFallDaysFlag) {
                    //连跌天数
                    if (closePriceList.get(i) < closePriceList.get(i - 1)) {
                        continueFallDays++;
                    } else {
                        continueFallDaysFlag = false;
                        //连跌幅度
                        statistics.setContinueFallRate((closePrice - closePriceList.get(i)) / closePriceList.get(i) * 100);
                    }
                }

                if (continueRiseStopDaysFlag) {
                    // 连续涨停
                    if (NumberUtil.round(closePriceList.get(i - 1) * 1.1, 2).equals(NumberUtil.round(closePriceList.get(i), 2))) {
                        continueRiseStopDays++;
                    } else {
                        continueRiseStopDaysFlag = false;
                        //连涨幅度
                        statistics.setContinueRiseRate((closePrice - closePriceList.get(i)) / closePriceList.get(i) * 100);
                    }
                }
                if (continueFallStopDaysFlag) {
                    if (NumberUtil.round(closePriceList.get(i - 1) * 0.9, 2).equals(NumberUtil.round(closePriceList.get(i), 2))) {
                        continueFallStopDays++;
                    } else {
                        continueFallStopDaysFlag = false;
                        //连跌幅度
                        statistics.setContinueFallRate((closePrice - closePriceList.get(i)) / closePriceList.get(i) * 100);
                    }
                }

            }
            statistics.setRiseDays(riseDays);
            statistics.setFallDays(fallDays);
            statistics.setRiseStopDays(riseStopDays);
            statistics.setFallStopDays(fallStopDays);

            statistics.setContinueRiseDays(continueRiseDays);
            statistics.setContinueFallDays(continueFallDays);
            statistics.setContinueFallStopDays(continueFallStopDays);
            statistics.setContinueRiseStopDays(continueRiseStopDays);
        }
    }

    /**
     * 设置均线值
     *
     * @param closePriceList 收盘价
     * @param n              均线天数
     * @return 均线值
     */
    private double getAverageLine(List<Double> closePriceList, int n) {
        int size = closePriceList.size();
        if (closePriceList.size() > n) {
            double sum = 0;
            for (int i = closePriceList.size() - 1; i >= 0; i--) {
                if ((size - i) <= n) {
                    sum += closePriceList.get(i);
                }
            }
            return NumberUtil.round(sum / n, 2).doubleValue();
        } else {
            return 0;
        }
    }

    /**
     * 设置类型,默认1000,1涨停2跌停3光头4光头光脚
     */
    private Integer getType(List<Double> openPriceList, List<Double> closePriceList, List<Double> highestList, List<Double> lowestList) {
        if (closePriceList == null || closePriceList.size() < 2) {
            return 1000;
        }
        int size = closePriceList.size();
        //涨停
        if (NumberUtil.round(closePriceList.get(size - 2) * 1.1, 2).equals(NumberUtil.round(closePriceList.get(size - 1), 2))) {
            return 1;
        }
        //光头光脚大阳线
        if (openPriceList.get(size - 1).equals(lowestList.get(size - 1))
                && closePriceList.get(size - 1).equals(highestList.get(size - 1))
                && (closePriceList.get(size - 1) - openPriceList.get(size - 1)) / openPriceList.get(size - 1) > 0.07) {
            return 2;
        }
        //光头光脚
        if (openPriceList.get(size - 1).equals(lowestList.get(size - 1))
                && closePriceList.get(size - 1).equals(highestList.get(size - 1))) {
            return 3;
        }
        //光头
        if (closePriceList.get(size - 1).equals(highestList.get(size - 1))) {
            return 4;
        }
        //跌停
        if (NumberUtil.round(closePriceList.get(size - 2) * 0.9, 2).equals(NumberUtil.round(closePriceList.get(size - 1), 2))) {
            return 5;
        }
        return 1000;
    }

    /**
     * 获取所有股票一段天数内的历史股价和成交量
     */
    private List<StockHistoryPriceDTO> getStockHistoryPriceDTO(Date startDate, Date endDate) {
        List<StockHistoryPriceDTO> result = new ArrayList<>();
        List<StockPrice> stockPriceList = null;
        Date s = new Date();
        try {
            logger.info("开始获取所有股票一段天数内的历史股价");
            stockPriceList = stockPriceService.selectStockPriceByStartAndEndDate(startDate, endDate);
        } catch (Exception e) {
            logger.error("查询数据库失败!");
            e.printStackTrace();
        }
        logger.info("用时:" + DateUtil.betweenMs(s, new Date()));

        String pointStockCode = "-1";
        StockHistoryPriceDTO dto = null;
        if (stockPriceList == null) return null;
        for (StockPrice item : stockPriceList) {
            if (!item.getStockCode().equals(pointStockCode)) {
                //与指针的股票编码不同,为新的股票数据,给指针重新赋值
                pointStockCode = item.getStockCode();
                dto = new StockHistoryPriceDTO();
                dto.setStockCode(item.getStockCode());
                result.add(dto);
            }
            assert dto != null;
            dto.getOpenPriceList().add(item.getOpenPrice());
            dto.getClosePriceList().add(item.getClosePrice());
            dto.getHighestList().add(item.getHighest());
            dto.getLowestList().add(item.getLowest());
            dto.getPriceList().add(new ArrayList<>(Arrays.asList(item.getOpenPrice(), item.getClosePrice(), item.getHighest(), item.getLowest())));
            dto.getVolumeList().add(item.getVolume());
        }
        return result;
    }

    /**
     * 统计当天内的股票涨跌数量
     */
    @Override
    public void statisticsStockRiseNum() {
        Date now = DateUtil.date();
        //获取最近两天的股票交易日期
        Date[] recentDate = tradeDateUtils.selectStockTradeStartAndEndDate(2);
        Date previousTradeDate = recentDate[0];
        Date lastTradeDate = recentDate[1];

        boolean isSameDay = DateUtil.isSameDay(lastTradeDate, now);

        //获取最近两天的股票交易价格
        List<StockPrice> twoDayStockPrice = stockPriceService.selectStockPriceByStartAndEndDate(previousTradeDate, lastTradeDate);

        int riseNum = 0;
        int riseStopNum = 0;
        int fallStopNum = 0;
        StockPrice current;
        // 从第二条记录开始遍历,后一个和前一个比较,如果不相等,则更新该股票的当前价和涨跌幅
        for (int i = 0; i < twoDayStockPrice.size() - 2; i++) {
            current = twoDayStockPrice.get(i);
            StockPrice next = twoDayStockPrice.get(i + 1);
            //当前和下一个是相同股票编码
            if (current.getStockCode().equals(next.getStockCode())) {

                if (DateUtil.date(next.getTradeDate()).isBefore(current.getTradeDate())) {
                    // 交换日期
                    StockPrice currentTmp = new StockPrice();
                    BeanUtils.copyBeanProp(currentTmp, current);
                    StockPrice nextTmp = new StockPrice();
                    BeanUtils.copyBeanProp(nextTmp, next);
                    current = nextTmp;
                    next = currentTmp;
                }

                if (next.getClosePrice() - current.getClosePrice() > 0) {
                    riseNum++;
                }

                //涨停
                if (NumberUtil.round(current.getClosePrice() * 1.1, 2).equals(NumberUtil.round(next.getClosePrice(), 2))) {
                    riseStopNum++;
                }

                //跌停
                if (NumberUtil.round(current.getClosePrice() * 0.9, 2).equals(NumberUtil.round(next.getClosePrice(), 2))) {
                    fallStopNum++;
                }
            }
        }

        stockRiseFallNumService.deleteStockRiseFallNumByTradeDate(DateUtil.beginOfDay(new Date()));
        StockRiseFallNum stockRiseFallNum = new StockRiseFallNum();
        if (isSameDay) {
            // 今天是交易日
            stockRiseFallNum.setRiseNum(riseNum);
            stockRiseFallNum.setFallNum(stockService.selectStockCount() - riseNum);
            stockRiseFallNum.setRiseStopNum(riseStopNum);
            stockRiseFallNum.setFallStopNum(fallStopNum);
            stockRiseFallNum.setTradeDate(now);
            stockRiseFallNum.setCreateTime(DateUtil.date());
            stockRiseFallNumService.insertStockRiseFallNum(stockRiseFallNum);
        }

    }

    /**
     * 检查接口是否可用
     */
    @Override
    public void checkApiIsOk() {
        List<StockInterface> list = stockInterfaceService.selectStockInterfaceList(new StockInterface());
        for (StockInterface stockInterface : list) {
            stockInterfaceService.testInterface(stockInterface);
        }
    }

    /**
     * 备份历史价格数据
     */
    @Override
    public void backupStockHistoryPrice() {
        List<Stock> stockList = stockService.selectStockList(new Stock());
        for (Stock stock : stockList) {
            StockPrice p = new StockPrice();
            p.setStockCode(stock.getStockCode());
            List<StockPrice> stockPriceList = stockPriceService.selectStockPriceList(p);
            if (stockPriceList == null || stockPriceList.size() == 0) {
                continue;
            }
            stockPriceMapper.backupStockHistoryPrice(stockPriceList);
        }
    }
}
